<?php
/**
 * @link http://www.tintsoft.com/
 * @copyright Copyright (c) 2013 TintSoft LLC
 * @license http://www.tintsoft.com/license/
 */
namespace Leaps\Stdlib;
/**
 * 事件监听调度类
 *
 * @author Tongle Xu <xutongle@gmail.com>
 * @since 4.0
 */
class Event {

    /**
     * 已经注册的监听器
     *
     * @var array
     */
    protected static $listeners = [];

    /**
     * 已经注册的通配符监听器
     *
     * @var array
    */
    protected static $wildcards = [];

    /**
     * 排序过的监听器
     *
     * @var array
    */
    protected static $sorted = [];

    /**
     * 注册一个事件监听到调度器
     *
     * @param  string  $event
     * @param  mixed   $listener
     * @param  int     $priority
     * @return void
     */
    public static function listen($event, $listener, $priority = 0)
    {
        if (static::contains($event, '*'))
        {
            return static::setupWildcardListen($event, $listener, $priority = 0);
        }
        static::$listeners[$event][$priority][] = static::makeListener($listener);
        unset(static::$sorted[$event]);
    }

    /**
     * 设置通配符听众
     *
     * @param  string  $event
     * @param  mixed   $listener
     * @param  int     $priority
     * @return void
     */
    protected static function setupWildcardListen($event, $listener, $priority)
    {
        static::$wildcards[$event][] = $listener;
    }

    /**
     * 判断指定事件的听众是否已注册
     *
     * @param  string  $eventName 事件名称
     * @return bool
     */
    public static function hasListeners($eventName)
    {
        return isset(static::$listeners[$eventName]);
    }

    /**
     * 注册一个队列事件
     *
     * @param  string  $event
     * @param  array   $payload
     * @return void
     */
    public static function queue($event, $payload = array())
    {
        static::listen($event.'_queue', function() use ($event, $payload)
        {
            static::trigger($event, $payload);
        });
    }

    /**
     * 触发一个队列事件
     *
     * @param  string  $event
     * @return void
     */
    public static function flush($event)
    {
        static::trigger($event.'_queue');
    }

    /**
     * 获取到第一条非null的返回数据之前将按顺序执行所有监听某一事件的监听器。
     *
     * @param  string  $event
     * @param  array   $payload
     * @return mixed
     */
    public static function until($event, $payload = array())
    {
        return static::trigger($event, $payload, true);
    }

    /**
     * 触发事件并调用所有的监听器
     *
     * @param  string  $event
     * @param  mixed   $payload
     * @param  boolean $halt
     * @return void
     */
    public static function trigger($event, $payload = array(), $halt = false)
    {
        $responses = array();
        if ( ! is_array($payload)) $payload = array($payload);
        $payload[] = $event;
        foreach (static::getListeners($event) as $listener)
        {
            $response = call_user_func_array($listener, $payload);
           if ( ! is_null($response) and $halt)
            {
                return $response;
            }
            if ($response === false) break;
            $responses[] = $response;
        }
        return $halt ? null : $responses;
    }

    /**
     * 获取指定事件所有的听众
     *
     * @param  string  $eventName
     * @return array
     */
    public static function getListeners($eventName)
    {
        $wildcards = static::getWildcardListeners($eventName);
        if ( ! isset(static::$sorted[$eventName]))
        {
            static::sortListeners($eventName);
        }
        return array_merge(static::$sorted[$eventName], $wildcards);
    }

    /**
     * 获取所有通配符听众
     *
     * @param  string  $eventName
     * @return array
     */
    protected static function getWildcardListeners($eventName)
    {
        $wildcards = array();
        foreach (static::$wildcards as $key => $listeners)
        {
            if (static::is($key, $eventName)) $wildcards = array_merge($wildcards, $listeners);
        }
        return $wildcards;
    }

    /**
     * 对事件听众按优先级排序
     *
     * @param  string  $eventName
     * @return array
     */
    protected static function sortListeners($eventName)
    {
        static::$sorted[$eventName] = array();
        if (isset(static::$listeners[$eventName]))
        {
            krsort(static::$listeners[$eventName]);
            static::$sorted[$eventName] = call_user_func_array('array_merge', static::$listeners[$eventName]);
        }
    }

    /**
     * 注册一个事件监听器和分配器。
     *
     * @param  mixed   $listener
     * @return void
     */
    public static function makeListener($listener)
    {
        if (is_string($listener))
        {
            $listener = static::createClassListener($listener);
        }
        return $listener;
    }

    /**
     * 创建一个基于类的事件监听器
     *
     * @param  mixed    $listener
     * @return Closure
     */
    public static function createClassListener($listener)
    {
        return function() use ($listener)
        {
            $segments = explode('::', $listener);
            $method = count($segments) == 2 ? $segments[1] : 'handle';
            $callable = [new $segments[0] , $method];
            $data = func_get_args();
            return call_user_func_array($callable, $data);
        };
    }

    /**
     * 删除指定事件所有的听众
     *
     * @param  string  $event
     * @return void
     */
    public static function forget($event)
    {
        unset(static::$listeners[$event]);
        unset(static::$sorted[$event]);
    }

    /**
     * 判断字符串中是否包含另一个字符串
     */
    protected static function contains($haystack, $needle)
    {
        foreach ((array) $needle as $n)
        {
            if (strpos($haystack, $n) !== false) return true;
        }
        return false;
    }

    protected static function is($pattern, $value)
    {
        if ($pattern == $value) return true;
        $pattern = preg_quote($pattern, '#');
        if ($pattern !== '/')
        {
            $pattern = str_replace('\*', '.*', $pattern).'\z';
        }
        else
        {
            $pattern = '/$';
        }
        return (bool) preg_match('#^'.$pattern.'#', $value);
    }
}